home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1996 April: Mac OS SDK / Dev.CD Apr 96 SDK / Dev.CD Apr 96 SDK1.toast / Development Kits (Disc 1) / AppleScript / Development Tools / Sample Code / 7Edit 3.1 / Sources / SVDrag.c < prev    next >
Encoding:
Text File  |  1995-11-20  |  26.0 KB  |  1,107 lines  |  [TEXT/MMCC]

  1. // SVDrag.c
  2. //
  3. // 7Edit 3.1d1. Original version by Jon Lansdell and Nigel Humphreys.
  4. // 3.1 updates by Greg Sutton.
  5. // Drag Manager support by Chris White
  6. // ©Apple Computer Inc 1995, all rights reserved.
  7.  
  8. #include "SVDrag.h"
  9. #include "SVEditWindow.h"
  10. #include "SVEditGlobals.h"
  11. #include "Offscreen.h"
  12.  
  13. #include "DebugUtils.h"
  14.  
  15.  
  16. #include <Drag.h>
  17. #include <LowMem.h>
  18. #include <Errors.h>
  19. #include <Folders.h>
  20.  
  21.  
  22.  
  23. static short    gCaretOffset;        // Caret drawn during a drag
  24.  
  25.  
  26.  
  27. #pragma segment Drag
  28.  
  29. //
  30. // InitDragHandlers creates the UPPs for the Drag Manager 
  31. // callback routines _if_ the Drag Manager is available.
  32. //
  33. OSErr InitDragHandlers ( void )
  34. {
  35.     OSErr    theErr = noErr;
  36.     
  37.     if ( gHasDragManager )
  38.     {
  39.         gDragTrackingHandlerUPP = NewDragTrackingHandlerProc ( MyTrackingHandler );
  40.         gDragReceiveHandlerUPP = NewDragReceiveHandlerProc ( MyReceiveHandler );
  41.     }
  42.     
  43.     return theErr;
  44. }
  45.  
  46.  
  47.  
  48. //
  49. // InstallDragHandlers attaches the tracking and receive handlers to
  50. // one of the application's windows.
  51. //
  52. OSErr InstallDragHandlers ( WindowRef theWindow )
  53. {    
  54.     OSErr     theErr = noErr;
  55.     
  56.     if ( gHasDragManager )
  57.     {
  58.         
  59.         theErr = InstallTrackingHandler ( gDragTrackingHandlerUPP, theWindow, nil );
  60.     
  61.         if ( theErr == noErr )
  62.         {
  63.             theErr = InstallReceiveHandler ( gDragReceiveHandlerUPP, theWindow, nil );
  64.             if ( theErr )
  65.                 (void) RemoveTrackingHandler ( gDragTrackingHandlerUPP, theWindow );
  66.         }
  67.         
  68.     }
  69.     
  70.     return theErr;
  71. }
  72.  
  73.  
  74.  
  75. //
  76. // RemoveDragHandlers removes the tracking and receive handlers from
  77. // one of the application's windows (usually just prior to disposal).
  78. //
  79. void RemoveDragHandlers ( WindowRef theWindow )
  80. {
  81.     if ( gHasDragManager )
  82.     {
  83.         
  84.         RemoveReceiveHandler ( gDragReceiveHandlerUPP, theWindow );
  85.         RemoveTrackingHandler ( gDragTrackingHandlerUPP, theWindow );
  86.         
  87.     }
  88.     
  89.     return;
  90. }
  91.  
  92.  
  93.  
  94. Boolean IsDragInWindowContent ( DragReference theDrag, WindowRef theWindow )
  95. {
  96.     Point    localPt;
  97.     
  98.     GetDragMouse ( theDrag, &localPt, 0L );
  99.     GlobalToLocal ( &localPt );            // Assumes theWindow is current port
  100.     
  101.     return PtInWindow ( localPt, theWindow );
  102. }
  103.  
  104.  
  105.  
  106. Boolean PtInWindow ( Point localPt, WindowRef theWindow )
  107. {
  108.     DPtr theDocument;
  109.     
  110.     theDocument = DPtrFromWindowPtr ( theWindow );
  111.     return PtInDocument ( localPt, theDocument );
  112. }
  113.  
  114.  
  115.  
  116. Boolean PtInDocument ( Point localPt, DPtr theDocument )
  117. {
  118.     return PtInRect ( localPt, &(**(theDocument->theText)).viewRect );
  119. }
  120.  
  121.  
  122.  
  123. Boolean CanAcceptDragItems ( DragReference theDrag )
  124. {
  125.     OSErr            theErr;
  126.     ItemReference    theRef;
  127.     Boolean            bCanAccept = false;
  128.     HFSFlavor         currHFSFlavor;
  129.     Size            flavorDataSize;
  130.     FlavorFlags        currFlavorFlags;
  131.     
  132.     
  133.     
  134.     theErr = GetDragItemReferenceNumber ( theDrag, 1, &theRef );
  135.     if ( theErr == noErr )
  136.     {
  137.         // use GetFlavorFlags to check on flavor existence of TEXT data.
  138.         theErr = GetFlavorFlags ( theDrag, theRef, 'TEXT', &currFlavorFlags );
  139.         if ( theErr == noErr )
  140.             bCanAccept = true;
  141.         else
  142.         {
  143.             // check if the item is a file spec, and it contains TEXT
  144.             flavorDataSize = sizeof ( HFSFlavor );
  145.             theErr = GetFlavorData ( theDrag, theRef, flavorTypeHFS, &currHFSFlavor,
  146.                                             &flavorDataSize, 0    );
  147.             
  148.             if ( theErr == noErr && currHFSFlavor.fileType == 'TEXT' ) 
  149.                 bCanAccept = true;
  150.         }
  151.     }
  152.     
  153.     return bCanAccept;
  154. }
  155.  
  156.  
  157.  
  158. pascal OSErr MyTrackingHandler ( DragTrackingMessage theMessage, WindowPtr theWindow,
  159.                                     void* handlerRefCon, DragReference theDrag )
  160. {
  161. #pragma unused(handlerRefCon)
  162.  
  163.     static Boolean    bHasAcceptableDrag;
  164.     static Boolean    bHasHilitedWindow;
  165.     static Boolean    bShowCaret;
  166.     
  167.     static long        caretMovedTime;
  168.     static short    lastOffset, insertPosition;
  169.     
  170.     short            theOffset;
  171.     OSErr            theErr = noErr;
  172.     long            theTime = TickCount ( );
  173.     unsigned long    theAttributes;
  174.     RgnHandle        tempRgn;
  175.     DPtr            theDocument, hitDocument;
  176.     
  177.     
  178.     GetDragAttributes ( theDrag, &theAttributes );
  179.     theDocument = DPtrFromWindowPtr ( theWindow );
  180.     
  181.     switch ( theMessage )
  182.     {
  183.         case dragTrackingEnterHandler:
  184.             // Any initialization for this window handler.
  185.             bHasAcceptableDrag = CanAcceptDragItems ( theDrag );
  186.             
  187.             // Let the drag manager know if we can't accept this drag
  188.             if ( !bHasAcceptableDrag )
  189.                 theErr = dragNotAcceptedErr;
  190.         break;
  191.             
  192.         case dragTrackingEnterWindow: 
  193.             
  194.             caretMovedTime = theTime;
  195.             gCaretOffset = lastOffset = -1;
  196.             bShowCaret = true;
  197.  
  198.             bHasHilitedWindow = false;
  199.             
  200.         break;
  201.         
  202.         case dragTrackingInWindow:
  203.             // Hiliting of the window during a drag is done
  204.             // here.  Do it only if we can accept these items
  205.             // and we're not in the source window.
  206.             
  207.             if ( bHasAcceptableDrag )
  208.             {
  209.                 // Check if the mouse is in a window's content region. Make an
  210.                 // exception if the window has yet to leave the source window,
  211.                 // since we don't want to hilite it in that stuation.
  212.                 Boolean        bMouseInContent;
  213.                 Point        theMouse;
  214.                 
  215.                 bMouseInContent = false;
  216.                 if ( theAttributes & dragHasLeftSenderWindow )
  217.                     bMouseInContent = IsDragInWindowContent ( theDrag, theWindow );
  218.                 
  219.                 // If the mouse is in a window and it isn't hilited...
  220.                 if ( bMouseInContent && !bHasHilitedWindow )
  221.                 {
  222.                     tempRgn = NewRgn ( );
  223.                     RectRgn ( tempRgn, &(**(theDocument->theText)).viewRect );
  224.                     
  225.                     // ...draw the hilight...
  226.                     if ( ShowDragHilite ( theDrag, tempRgn, true ) == noErr )
  227.                         // ... and remember it's now hilited
  228.                         bHasHilitedWindow = true;
  229.                     
  230.                     DisposeRgn ( tempRgn );
  231.                 }
  232.                 
  233.                 
  234.                 GetDragMouse ( theDrag, &theMouse, 0L );
  235.                 theOffset = HitTest ( theMouse, &hitDocument );
  236.                 
  237.                 if ( theDocument == hitDocument )
  238.                 {
  239.                 
  240.                     // Do not allow tracking through the selection in the
  241.                     // window that sourced the drag.
  242.                     if ( theAttributes & dragInsideSenderWindow )
  243.                         if ( IsOffsetInSelection ( theOffset, theDocument->theText ) )
  244.                             theOffset = -1;
  245.                         
  246.                     
  247.                     
  248.                     insertPosition = theOffset;
  249.                     
  250.                     //    Reset flashing counter if the offset has moved. This makes the
  251.                     //    caret blink only after the caret has stopped moving long enough.
  252.                     if ( theOffset != lastOffset )
  253.                     {
  254.                         caretMovedTime = theTime;
  255.                         bShowCaret = true;
  256.                     }
  257.                     lastOffset = theOffset;
  258.                     
  259.                     
  260.                     //    Flash caret.
  261.                     if ( theTime - caretMovedTime > LMGetCaretTime ( ) )
  262.                     {
  263.                         bShowCaret = !bShowCaret;
  264.                         caretMovedTime = theTime;
  265.                     }
  266.                     
  267.                     if ( !bShowCaret )
  268.                         theOffset = -1;
  269.                         
  270.                     //    If caret offset has changed, move caret on screen.
  271.                     if (theOffset != gCaretOffset)
  272.                     {
  273.                         if (gCaretOffset != -1)
  274.                             DrawCaret(gCaretOffset, theDocument->theText);
  275.                         
  276.                         if (theOffset != -1)
  277.                             DrawCaret(theOffset, theDocument->theText);
  278.                     }
  279.                     
  280.                     gCaretOffset = theOffset;
  281.                 }
  282.                 else
  283.                 {
  284.                     lastOffset = theOffset;
  285.                     insertPosition = -1;
  286.                 }
  287.  
  288.             }
  289.         break;
  290.         
  291.         case dragTrackingLeaveWindow:
  292.             //    If the caret is on the screen, remove it.
  293.             if (gCaretOffset != -1)
  294.             {
  295.                 DrawCaret(gCaretOffset, theDocument->theText);
  296.                 gCaretOffset = -1;
  297.             }
  298.             
  299.             // Remove any window hiliting here. If the mouse
  300.             // is not in the window and it's hilited...
  301.             if ( bHasHilitedWindow )
  302.                 // ...erase the hilight...
  303.                 if ( HideDragHilite ( theDrag ) == noErr )
  304.                     // ...remember that nothing is hilited
  305.                     bHasHilitedWindow = false;
  306.         break;
  307.         
  308.         case dragTrackingLeaveHandler:
  309.         break;
  310.         
  311.         default:
  312.             theErr = paramErr;
  313.         break;
  314.     }
  315.     
  316.     return theErr;
  317. }
  318.  
  319.  
  320.  
  321. Boolean IsOffsetInSelection ( short theOffset, TEHandle theTE )
  322. {
  323.     return (theOffset >= (*theTE)->selStart && theOffset <= (*theTE)->selEnd);
  324. }
  325.  
  326.  
  327.  
  328. //
  329. // DragIsNotInSourceWindow returns true if the drag in progress
  330. // is not in the same window it originated in. This is called by
  331. // the tracking and receive handlers.
  332. //
  333. static Boolean DragIsNotInSourceWindow ( DragReference theDrag )
  334. {
  335.     DragAttributes currDragFlags;
  336.     
  337.     GetDragAttributes ( theDrag, &currDragFlags );
  338.     return !(currDragFlags & dragInsideSenderWindow);
  339. }
  340.  
  341.  
  342.  
  343. //
  344. // DragReceiver is called by the drag manager whenever an
  345. // item is dropped on one of the application's windows.
  346. //
  347. pascal OSErr MyReceiveHandler ( WindowPtr theWindow, void* handlerRefCon, 
  348.                                     DragReference theDrag )
  349. {
  350. #pragma unused(handlerRefCon)
  351.  
  352.     Boolean                bHaveData = false;
  353.     Boolean                bMove = false;
  354.     OSErr                theErr = noErr;
  355.     
  356.     
  357.     unsigned short        numItems;
  358.     int                    i;
  359.     unsigned long        theAttributes;
  360.     ItemReference        theItem;
  361.     Size                textSize, styleSize;
  362.     DPtr                theDocument;
  363.     
  364.     short                theOffset;
  365.     Point                thePoint = { 0, 0 };
  366.     
  367.     Ptr                    textData = nil;
  368.     StScrpHandle        styleHandle = nil;
  369.  
  370.     TEHandle            tempTE = nil;
  371.     Rect                tempRect;
  372.  
  373.     
  374.     
  375.     RgnHandle            tempRgn;
  376.     Rect                sourceRect, targetRect;
  377.  
  378.     tWindowOffscreen*    theOffscreen = nil;
  379.     
  380.     
  381.     
  382.     
  383.     if ( !(CanAcceptDragItems ( theDrag ) && IsDragInWindowContent ( theDrag, theWindow )) )
  384.         return dragNotAcceptedErr;
  385.     
  386.     theDocument = DPtrFromWindowPtr ( theWindow );
  387.     GetDragAttributes ( theDrag, &theAttributes );
  388.     
  389.     // Get the location of the drop
  390.     theErr = GetDragMouse ( theDrag, &thePoint, 0L );
  391.     if ( theErr ) goto CleanupAndBail;
  392.     
  393.     GlobalToLocal ( &thePoint );            // Assumes theWindow is current port
  394.     // Map the drop location to the text
  395.     theOffset = TEGetOffset ( thePoint, theDocument->theText );
  396.  
  397.     // Don't allow a drop within the original selection
  398.     if ( theAttributes & dragInsideSenderWindow )
  399.         if ( IsOffsetInSelection ( theOffset, theDocument->theText ) )
  400.             return dragNotAcceptedErr;
  401.             
  402.     
  403.     // Is this drag copying or a moving the text?
  404.     bMove = IsDragMoving ( theDrag );
  405.     
  406.     // First, gather all the text into a temp TE record, and then insert that into
  407.     // the doucment. This approach makes it easy to handle things like text selection.
  408.     SetRect ( &tempRect, 0, 0, 0, 0 );
  409.     tempTE = TEStylNew ( &tempRect, &tempRect );
  410.     theErr = MemError ( );
  411.     if ( tempTE == nil || theErr ) goto CleanupAndBail;
  412.     
  413.     theErr = CountDragItems ( theDrag, &numItems );
  414.     if ( theErr ) goto CleanupAndBail;
  415.     
  416.     for ( i = 1; i <= numItems; i++ )
  417.     {
  418.         
  419.         theErr = GetDragItemReferenceNumber ( theDrag, i, &theItem );
  420.         if ( theErr ) goto CleanupAndBail;
  421.         
  422.         theErr = GetFlavorDataSize ( theDrag, theItem, 'TEXT', &textSize );
  423.         if ( theErr ) goto CleanupAndBail;
  424.         
  425.         textData = NewPtr ( textSize );
  426.         theErr = MemError ( );
  427.         if ( textData == nil || theErr ) goto CleanupAndBail;
  428.         
  429.         theErr = GetFlavorData ( theDrag, theItem, 'TEXT', textData, &textSize, 0L );
  430.         if ( theErr ) goto CleanupAndBail;
  431.         
  432.         
  433.         // Check for optional styl data for the TEXT.
  434.         styleHandle = 0L;
  435.         theErr = GetFlavorDataSize ( theDrag, theItem, 'styl', &styleSize );
  436.         if ( !theErr )
  437.         {
  438.             styleHandle = (StScrpHandle) NewHandle ( styleSize );
  439.             theErr = MemError ( );
  440.             if ( styleHandle == nil || theErr ) goto CleanupAndBail;
  441.             
  442.             HLock ( (Handle) styleHandle );
  443.             theErr = GetFlavorData ( theDrag, theItem, 'styl', *styleHandle, &styleSize, 0L );
  444.             if ( theErr ) goto CleanupAndBail;
  445.             HUnlock ( (Handle) styleHandle );
  446.         }
  447.         
  448.         // Insert this drag item's text into the tempTE.
  449.         TESetSelect ( 32767, 32767, tempTE );
  450.         TEStylInsert ( textData, textSize, styleHandle, tempTE );
  451.         
  452.         DisposePtr ( textData );
  453.         textData = nil;
  454.         if ( styleHandle )
  455.         {
  456.             DisposeHandle ( (Handle) styleHandle );
  457.             styleHandle = nil;
  458.         }
  459.         
  460.     }
  461.     
  462.     
  463.     // Pull the TEXT and styl data out of the tempTE handle.
  464.     textData = NewPtr (textSize = (**tempTE).teLength );
  465.     theErr = MemError ( );
  466.     if ( textData == nil || theErr ) goto CleanupAndBail;
  467.     
  468.     BlockMoveData ( *(*tempTE)->hText, textData, textSize );
  469.     TESetSelect ( 0, 32767, tempTE );
  470.     styleHandle = TEGetStyleScrapHandle (tempTE );
  471.     TEDispose ( tempTE );
  472.     tempTE = nil;
  473.     
  474.     
  475.     // Insert any text into the destination.
  476.     if ( textSize != 0 )
  477.     {
  478.         
  479.         // Get rid of the hilite/caret before inserting
  480.         if ( theAttributes & dragHasLeftSenderWindow )
  481.             HideDragHilite ( theDrag );
  482.         
  483.         if ( gCaretOffset != -1 )
  484.         {
  485.             DrawCaret ( gCaretOffset, theDocument->theText );
  486.             gCaretOffset = -1;
  487.         }
  488.         
  489.         //    If the drag occurred completely within the same window and the window is not
  490.         //    frontmost, bring the window forward and update its contents before completing
  491.         //    the drag.
  492.         if ( (theAttributes & dragInsideSenderWindow) && (theWindow != FrontWindow ( )) )
  493.         {
  494.             SelectWindow ( theWindow );
  495.             DoUpdate ( theWindow );
  496.             TEActivate ( theDocument->theText );
  497.         }
  498.         
  499.         //    If the window is not active, must activate TE before inserting
  500.         //    text or the background hilite will not update correctly.
  501.         if ( !IsWindowHilited ( theWindow ) )
  502.             TEActivate ( theDocument->theText );
  503.         
  504.         //    Draw everything into offscreen pixmap.
  505.         theOffscreen = DrawOffscreen ( theWindow );
  506.         if ( theOffscreen  )
  507.             (*theDocument->theText)->inPort = (GrafPtr) theOffscreen->offscreenWorld;
  508.         
  509.         if ( bMove )
  510.         {
  511.             // Get the current hilite rgn for zooming (source)
  512.             tempRgn = NewRgn ( );
  513.             theErr = MemError ( );
  514.             if ( theErr )    goto CleanupAndBail;
  515.             GetSelectedTextRgn ( theDocument, tempRgn );
  516.             sourceRect = (*tempRgn)->rgnBBox;
  517.             LocalRectToGlobalRect ( &sourceRect, theWindow );
  518.             
  519.             // If this is a move operation, delete the old text.
  520.             DeleteTextSelection ( theDocument->theText, &theOffset );
  521.         }
  522.         
  523.         InsertTextAtOffset ( theOffset, textData, textSize, styleHandle, theDocument->theText );
  524.         
  525.         // If the text is moving (not copying) within the same window, provide a ZoomRects
  526.         // from the source to the destination before revealing the reflowed text.
  527.         
  528.         if ( bMove )
  529.         {
  530.             // Get the current hilite rgn for zooming (target)
  531.             GetSelectedTextRgn ( theDocument, tempRgn );
  532.             targetRect = (*tempRgn)->rgnBBox;
  533.             DisposeRgn ( tempRgn );
  534.             tempRgn = nil;
  535.             LocalRectToGlobalRect ( &targetRect, theWindow );
  536.             
  537.             ZoomRects ( &sourceRect, &targetRect, 12, zoomDecelerate );
  538.         }
  539.     }
  540.     
  541.     // Undo the TEActivate, if needed.
  542.     if ( !IsWindowHilited ( theWindow ) )
  543.         TEDeactivate ( theDocument->theText );
  544.     
  545.     //    Show the offscreen bitmap.
  546.     if ( theOffscreen )
  547.     {
  548.         theOffscreen = DrawOnscreen ( theOffscreen );
  549.         (*theDocument->theText)->inPort = theWindow;
  550.     }
  551.     
  552.     // Make the document dirty
  553.     theDocument->dirty = true;
  554.     
  555.     
  556. CleanupAndBail:
  557.     
  558.     
  559.     // All of these will be nil if no error occurred
  560.     if ( textData )
  561.         DisposePtr ( textData );
  562.     if ( styleHandle )
  563.         DisposeHandle ( (Handle) styleHandle );
  564.     if ( tempTE )
  565.         TEDispose ( tempTE );
  566.     if ( tempRgn )
  567.         DisposeRgn ( tempRgn );
  568.     
  569.     // Normally  nil since DrawOnscreen calls DisposeOffscreen
  570.     if ( theOffscreen )
  571.     {
  572.         DisposeOffscreen ( theOffscreen );
  573.         (*theDocument->theText)->inPort = theWindow;
  574.     }
  575.     
  576.     
  577.     return theErr;
  578. }
  579.  
  580.  
  581.  
  582. void DeleteTextSelection ( TEHandle theTE, short* theInsertPosition )
  583. {
  584.     short selStart, selEnd;
  585.     
  586.     selStart = (*theTE)->selStart;
  587.     selEnd = (*theTE)->selEnd;
  588.     if ( WhiteSpaceAtOffset ( selStart - 1, theTE ) &&
  589.             !WhiteSpaceAtOffset ( selStart, theTE ) &&
  590.             !WhiteSpaceAtOffset ( selEnd - 1, theTE ) &&
  591.              WhiteSpaceAtOffset ( selEnd, theTE ) )
  592.     {
  593.         
  594.         if ( GetCharAtOffset ( selEnd, theTE ) == ' ' )
  595.             (*theTE)->selEnd++;
  596.     }
  597.     
  598.     if ( *theInsertPosition > selStart )
  599.         *theInsertPosition -= ((*theTE)->selEnd - (*theTE)->selStart);
  600.     
  601.     TEDelete ( theTE );
  602.     
  603.     return;
  604. }
  605.  
  606.  
  607.  
  608. static Boolean IsDragMoving ( DragReference theDrag )
  609. {
  610.     DragAttributes    theAttributes;
  611.     
  612.     GetDragAttributes ( theDrag, &theAttributes );
  613.     return (theAttributes & dragInsideSenderWindow) && !IsDragWithOptionKey ( theDrag );
  614. }
  615.  
  616.  
  617.  
  618. static Boolean IsDragWithOptionKey ( DragReference theDrag )
  619. {
  620.     short    mouseDownModifiers, mouseUpModifiers;
  621.     
  622.     GetDragModifiers ( theDrag, 0L, &mouseDownModifiers, &mouseUpModifiers );
  623.     return (mouseDownModifiers & optionKey) | (mouseUpModifiers & optionKey);
  624. }
  625.  
  626.  
  627.  
  628. void OutlineRegion ( RgnHandle theRgn )
  629. {
  630.     RgnHandle tempRgn;
  631.     
  632.     tempRgn = NewRgn ( );
  633.     CopyRgn ( theRgn, tempRgn );
  634.     InsetRgn ( tempRgn, 1, 1 );
  635.     DiffRgn ( theRgn, tempRgn, theRgn );
  636.     DisposeRgn ( tempRgn );
  637.     
  638.     return;
  639. }
  640.  
  641.  
  642.  
  643. OSErr DoWindowContentDrag ( WindowPtr theWindow, EventRecord* theEvent )
  644. {
  645.     OSErr            theErr = noErr;
  646.     DragReference    theDrag = (unsigned long) nil;
  647.     RgnHandle        dragRgn = nil;
  648.     Ptr                dataPtr = nil;
  649.     StScrpHandle    styleHandle = nil;
  650.     short            dataSize;
  651.     
  652.     DPtr            theDocument;
  653.     Rect            dragBounds;
  654.     ItemReference    theItem;
  655.     
  656.     
  657.     // create a new drag
  658.     theErr = NewDrag ( &theDrag );
  659.     if ( theErr ) goto CleanupAndBail;
  660.     
  661.     // use the window ptr as item reference for the heck of it
  662.     theItem = (ItemReference) theWindow;
  663.     
  664.     // add the data to the drag
  665.     theDocument = DPtrFromWindowPtr ( theWindow );
  666.     dataPtr = NewPtr ( 0L );
  667.     theErr = GetSelectedText ( theDocument, dataPtr, &dataSize );
  668.     if ( theErr || dataSize == 0 )    goto CleanupAndBail;
  669.     
  670.     theErr = AddDragItemFlavor ( theDrag, theItem, 'TEXT', dataPtr, dataSize, 0 );
  671.     if ( theErr )    goto CleanupAndBail;
  672.     DisposePtr ( dataPtr );
  673.     dataPtr = nil;
  674.     
  675.     
  676.     // Add style data
  677.     styleHandle = TEGetStyleScrapHandle ( theDocument->theText );
  678.     HLock ( (Handle) styleHandle );
  679.     AddDragItemFlavor ( theDrag, theItem, 'styl', (Ptr) *styleHandle, GetHandleSize ( (Handle) styleHandle ), 0 );
  680.     HUnlock ( (Handle) styleHandle );
  681.     DisposeHandle ( (Handle) styleHandle );
  682.     styleHandle = nil;
  683.     
  684.     
  685.     // generate the bounds and region for the drag using the window's
  686.     // content rectangle
  687.     dragBounds = (**((WindowPeek) theWindow)->contRgn).rgnBBox;
  688.     theErr = SetDragItemBounds(theDrag, theItem, &dragBounds);
  689.     if ( theErr ) goto CleanupAndBail;
  690.     
  691.     dragRgn = NewRgn ( );
  692.     GetSelectedTextRgn ( theDocument, dragRgn );
  693.     LocalRgnToGlobalRgn ( dragRgn, nil );
  694.     OutlineRegion ( dragRgn );
  695.     
  696.     // do the drag and clean up
  697.     TrackDrag ( theDrag, theEvent, dragRgn );
  698.     
  699.     
  700.     if ( DragIsNotInSourceWindow ( theDrag ) )
  701.     {
  702.         AEDesc    dropLocation;
  703.         
  704.         // Get the drop location
  705.         GetDropLocation ( theDrag, &dropLocation );
  706.         if ( !IsDragWithOptionKey ( theDrag ) && DropLocationIsFinderTrash ( &dropLocation) )
  707.         {
  708.             // Delete the exact text. Don't call DeleteTextSelection
  709.             TEDelete ( theDocument->theText );
  710.             theDocument->dirty = true;
  711.         }
  712.         
  713.         AEDisposeDesc ( &dropLocation );
  714.     }
  715.     
  716.     
  717. CleanupAndBail:
  718.     
  719.     if ( theDrag )
  720.         DisposeDrag(theDrag);
  721.     
  722.     if ( dragRgn )
  723.         DisposeRgn ( dragRgn );
  724.     
  725.     // These should be nil
  726.     if ( dataPtr )
  727.         DisposePtr ( dataPtr );
  728.     if ( styleHandle )
  729.         DisposeHandle ( (Handle) styleHandle );
  730.     
  731.     
  732.     return theErr;
  733. }
  734.  
  735.  
  736.  
  737. void LocalRgnToGlobalRgn ( RgnHandle theRgn, WindowRef theWindow )
  738. {
  739.     GrafPtr        savePort;
  740.     Point        localPt, globalPt;
  741.     
  742.     
  743.     if ( theWindow )
  744.     {
  745.         GetPort ( &savePort );
  746.         SetPortWindowPort ( theWindow );
  747.     }
  748.     
  749.     localPt = globalPt = *(Point*) &(*theRgn)->rgnBBox;        // top left
  750.     LocalToGlobal ( &globalPt );
  751.     SubPt ( localPt, &globalPt );
  752.     OffsetRgn ( theRgn, globalPt.h, globalPt.v );
  753.     
  754.     if ( theWindow )
  755.         SetPort ( savePort );
  756.         
  757.     return;
  758. }
  759.  
  760.  
  761.  
  762. void LocalRectToGlobalRect ( Rect* theRect, WindowRef theWindow )
  763. {
  764.     GrafPtr        savePort;
  765.     Point        localPt, globalPt;
  766.     
  767.     
  768.     if ( theWindow )
  769.     {
  770.         GetPort ( &savePort );
  771.         SetPortWindowPort ( theWindow );
  772.     }
  773.     
  774.     localPt = globalPt = *(Point*) theRect;        // top left
  775.     LocalToGlobal ( &globalPt );
  776.     SubPt ( localPt, &globalPt );
  777.     OffsetRect ( theRect, globalPt.h, globalPt.v );
  778.     
  779.     if ( theWindow )
  780.         SetPort ( savePort );
  781.         
  782.     return;
  783. }
  784.  
  785.  
  786.  
  787.  
  788. OSErr GetSelectedText ( DPtr theDocument, Ptr dataPtr, short* dataSize )
  789. {
  790.     OSErr theErr;
  791.     TEHandle teHandle = theDocument->theText;
  792.     
  793.     *dataSize = (**(teHandle)).selEnd - (**(teHandle)).selStart;
  794.     if ( *dataSize )
  795.     {
  796.         SetPtrSize ( dataPtr, *dataSize );
  797.         theErr = MemError ( );
  798.         if ( theErr )
  799.             return theErr;
  800.         BlockMoveData ( *(**(teHandle)).hText + (**(teHandle)).selStart, dataPtr, *dataSize );
  801.     }
  802.     
  803.     return noErr;
  804. }
  805.  
  806.  
  807.  
  808. void GetSelectedTextRgn ( DPtr theDocument, RgnHandle dataRgn )
  809. {
  810.     TEGetHiliteRgn ( dataRgn, theDocument->theText );
  811.     
  812.     return;
  813. }
  814.  
  815.  
  816.  
  817. //
  818. // Does the user want to drag something? First, checks the click could
  819. // be a drag, then waits to see if the user starts to drag the text.
  820. // 
  821. Boolean UserWantsToDrag ( WindowRef theWindow, Point globalPt )
  822. {
  823.     Point    localPt;
  824.     
  825.     localPt = globalPt;
  826.     GlobalToLocal ( &localPt );            // Assumes theWindow is current port
  827.     if ( PointInWindowSelection ( localPt, theWindow ) )
  828.         return WaitMouseMoved ( globalPt );
  829.         
  830.     return false;
  831. }
  832.  
  833.  
  834.  
  835. //
  836. // Returns true if the local point is in the window's current selection
  837. //
  838. Boolean PointInWindowSelection ( Point localPt, WindowRef theWindow )
  839. {
  840.     Boolean        bHit;
  841.     RgnHandle    tempRgn;
  842.     DPtr        theDocument;
  843.     
  844.     theDocument = DPtrFromWindowPtr ( theWindow );
  845.     if ( !(PtInDocument ( localPt, theDocument )) )
  846.         return false;
  847.     
  848.     tempRgn = NewRgn ( );
  849.     GetSelectedTextRgn ( theDocument, tempRgn );
  850.     bHit = PtInRgn ( localPt, tempRgn );
  851.     DisposeRgn ( tempRgn );
  852.     
  853.     return bHit;
  854. }
  855.  
  856.  
  857.  
  858. //
  859. //    Given a point in global coordinates, HitTest returns a pointer to a
  860. //    document structure if the point is inside a document window on the screen.
  861. //    If the point is not inside a document window, HitTest return NULL in
  862. //    theDoc. If the point is in a doument window and also in the viewRect of
  863. //    the document's TextEdit field, HitTest also returns the offset into
  864. //    the text that corresponds to that point. If the point is not in the text,
  865. //    HitTest returns -1.
  866. //
  867. short HitTest(Point theLoc, DPtr* theDoc)
  868. {
  869.     GrafPtr        savePort;
  870.     WindowPtr    theWindow;
  871.     short        offset;
  872.     
  873.     *theDoc = 0L;
  874.     offset = -1;
  875.     
  876.     if (FindWindow(theLoc, &theWindow) == inContent)
  877.     {
  878.         if ( Ours ( theWindow ) )
  879.         {
  880.             *theDoc = DPtrFromWindowPtr ( theWindow );
  881.             GetPort ( &savePort );
  882.             SetPort(theWindow);
  883.             GlobalToLocal(&theLoc);
  884.             SetPort ( savePort );
  885.             
  886.             if ((PtInRect(theLoc, &(**((**theDoc).theText)).viewRect)) && 
  887.                 (PtInRect(theLoc, &(**((**theDoc).theText)).destRect))) {
  888.                 
  889.                 offset = TEGetOffset(theLoc, (**theDoc).theText);
  890.  
  891.                 if ((TEIsFrontOfLine(offset, (**theDoc).theText)) && (offset) &&            
  892.                         ((*((**((**theDoc).theText)).hText))[offset - 1] != 0x0D) &&
  893.                         (TEGetPoint(offset - 1, (**theDoc).theText).h < theLoc.h)) {
  894.                     offset--;
  895.                 }
  896.             }
  897.         }
  898.     }
  899.  
  900.     return(offset);
  901. }
  902.  
  903.  
  904.  
  905. // TEIsFrontOfLine, given an offset and a TextEdit handle, returns true if
  906. // the given offset is at the beginning of a line start.
  907. short TEIsFrontOfLine ( short offset, TEHandle theTE )
  908.  
  909. {    short        line = 0;
  910.  
  911.     if ((**theTE).teLength == 0)
  912.         return(true);
  913.  
  914.     if (offset >= (**theTE).teLength)
  915.         return( (*((**theTE).hText))[(**theTE).teLength - 1] == 0x0d );
  916.  
  917.     while ((**theTE).lineStarts[line] < offset)
  918.         line++;
  919.  
  920.     return( (**theTE).lineStarts[line] == offset );
  921. }
  922.  
  923.  
  924.  
  925. // TEGetLine, given an offset and a TextEdit handle, returns the line number
  926. // of the line that contains the offset.
  927. short TEGetLine ( short offset, TEHandle theTE )
  928.  
  929. {    short        line = 0;
  930.  
  931.     if (offset > (**theTE).teLength)
  932.         return((**theTE).nLines);
  933.  
  934.     while ((**theTE).lineStarts[line] < offset)
  935.         line++;
  936.     
  937.     return(line);
  938. }
  939.  
  940.  
  941.  
  942. //
  943. //    DrawCaret draws a caret in a TextEdit field at the given offset. DrawCaret
  944. //    expects the port to be set to the port that the TextEdit field is in.
  945. //    DrawCaret inverts the image of the caret onto the screen.
  946. //
  947. void DrawCaret(short offset, TEHandle theTE)
  948.  
  949. {    Point        theLoc;
  950.     short        theLine, lineHeight;
  951.  
  952.  
  953.     // Get the coordinates and the line of the offset to draw the caret.
  954.  
  955.     theLoc  = TEGetPoint(offset, theTE);
  956.     theLine = TEGetLine(offset, theTE);
  957.     
  958.     
  959.     // For some reason, TextEdit dosen't return the proper coordinates
  960.     // of the last offset in the field if the last character in the record
  961.     // is a carriage return. TEGetPoint returns a point that is one line
  962.     // higher than expected. The following code fixes this problem.
  963.     if ((offset == (**theTE).teLength) &&
  964.             (*((**theTE).hText))[(**theTE).teLength - 1] == 0x0D) {
  965.         theLoc.v += TEGetHeight(theLine, theLine, theTE);
  966.     }
  967.  
  968.     // Always invert the caret when drawing.
  969.     PenMode(patXor);
  970.  
  971.     //Get the height of the line that the offset points to.
  972.     lineHeight = TEGetHeight(theLine, theLine, theTE);
  973.  
  974.     // Draw the appropriate caret image.
  975.     MoveTo(theLoc.h - 1, theLoc.v - 1);
  976.     Line(0, 1 - lineHeight);
  977.  
  978.     PenNormal();
  979. }
  980.  
  981.  
  982.  
  983. char GetCharAtOffset(short offset, TEHandle theTE)
  984.  
  985. {
  986.     if (offset < 0)
  987.         return(0x0D);
  988.  
  989.     return(((char *) *((**theTE).hText))[offset]);
  990. }
  991.  
  992.  
  993.  
  994. Boolean WhiteSpace(char theChar)
  995.  
  996. {
  997.     return((theChar == ' ') || (theChar == 0x0D));
  998. }
  999.  
  1000.  
  1001.  
  1002. Boolean WhiteSpaceAtOffset(short offset, TEHandle theTE)
  1003.  
  1004. {    char        theChar;
  1005.  
  1006.     if ((offset < 0) || (offset > (**theTE).teLength - 1))
  1007.         return(true);
  1008.  
  1009.     theChar = ((char *) *((**theTE).hText))[offset];
  1010.     return((theChar == ' ') || (theChar == 0x0D));
  1011. }
  1012.  
  1013.  
  1014.  
  1015. void InsertTextAtOffset(short offset, char *theBuf, long size, StScrpHandle theStyl, TEHandle theTE)
  1016.  
  1017. {
  1018.     if (size == 0)
  1019.         return;
  1020.  
  1021.     // If inserting at the end of a word and the selection does not begin with
  1022.     // a space, insert a space before the insertion.
  1023.     if (!WhiteSpaceAtOffset(offset - 1, theTE) &&
  1024.          WhiteSpaceAtOffset(offset, theTE) &&
  1025.         !WhiteSpace(theBuf[0])) {
  1026.  
  1027.         TESetSelect(offset, offset, theTE);
  1028.         TEKey(' ', theTE);
  1029.         offset++;
  1030.     }
  1031.  
  1032.     //    If inserting at the beginning of a word and the selection does not end
  1033.     //    with a space, insert a space after the insertion.
  1034.     if ( WhiteSpaceAtOffset(offset - 1, theTE) &&
  1035.         !WhiteSpaceAtOffset(offset, theTE) &&
  1036.         !WhiteSpace(theBuf[size - 1])) {
  1037.  
  1038.         TESetSelect(offset, offset, theTE);
  1039.         TEKey(' ', theTE);
  1040.     }
  1041.     
  1042.     TESetSelect(offset, offset, theTE);
  1043.     TEStylInsert(theBuf, size, theStyl, theTE);
  1044.     TESetSelect(offset, offset + size, theTE);
  1045.     
  1046.     return;
  1047. }
  1048.  
  1049.  
  1050.  
  1051. //
  1052. // DropLocationIsFinderTrash returns true if the given dropLocation
  1053. // AEDesc is a descriptor of the Finder's Trash.
  1054. //
  1055. Boolean DropLocationIsFinderTrash ( AEDesc* dropLocation )
  1056.  
  1057. {    OSErr            result;
  1058.     AEDesc            dropSpec;
  1059.     FSSpecPtr        theSpec;
  1060.     CInfoPBRec        thePB;
  1061.     short            trashVRefNum;
  1062.     long            trashDirID;
  1063.     
  1064.     
  1065.     
  1066.     //    Coerce the dropLocation descriptor to an FSSpec. If there's no dropLocation or
  1067.     //    it can't be coerced into an FSSpec, then it couldn't have been the Trash.
  1068.     if ( (dropLocation->descriptorType != typeNull) &&
  1069.         (AECoerceDesc(dropLocation, typeFSS, &dropSpec) == noErr))
  1070.     {
  1071.  
  1072.         HLock(dropSpec.dataHandle);
  1073.         theSpec = (FSSpec *) *dropSpec.dataHandle;
  1074.  
  1075.         //    Get the directory ID of the given dropLocation object.
  1076.         thePB.dirInfo.ioCompletion = 0L;
  1077.         thePB.dirInfo.ioNamePtr = (StringPtr) &theSpec->name;
  1078.         thePB.dirInfo.ioVRefNum = theSpec->vRefNum;
  1079.         thePB.dirInfo.ioFDirIndex = 0;
  1080.         thePB.dirInfo.ioDrDirID = theSpec->parID;
  1081.         
  1082.         result = PBGetCatInfo(&thePB, false);
  1083.  
  1084.         HUnlock(dropSpec.dataHandle);
  1085.         AEDisposeDesc(&dropSpec);
  1086.         
  1087.         if ( result )
  1088.             return(false);
  1089.  
  1090.         //    If the result is not a directory, it cannot be the Trash.
  1091.         if (!(thePB.dirInfo.ioFlAttrib & (1 << 4)))
  1092.             return(false);
  1093.  
  1094.         //    Get information about the Trash folder.
  1095.         FindFolder ( theSpec->vRefNum, kTrashFolderType, kCreateFolder, &trashVRefNum, &trashDirID);
  1096.  
  1097.         //    If the directory ID of the dropLocation object is the same as the directory ID
  1098.         //    returned by FindFolder, then the drop must have occurred into the Trash.
  1099.         if ( thePB.dirInfo.ioDrDirID == trashDirID )
  1100.             return true;
  1101.     }
  1102.     
  1103.     
  1104.     return false;
  1105. }
  1106.  
  1107.